// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/webui/signin/profile_picker_handler.h"

#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/check.h"
#include "base/containers/cxx20_erase.h"
#include "base/feature_list.h"
#include "base/json/values_util.h"
#include "base/metrics/histogram_functions.h"
#include "base/notreached.h"
#include "base/strings/utf_string_conversions.h"
#include "base/trace_event/trace_event.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/metrics/first_web_contents_profiler_base.h"
#include "chrome/browser/new_tab_page/chrome_colors/chrome_colors_service.h"
#include "chrome/browser/new_tab_page/chrome_colors/generated_colors_info.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_attributes_entry.h"
#include "chrome/browser/profiles/profile_attributes_storage.h"
#include "chrome/browser/profiles/profile_avatar_icon_util.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/profiles/profile_statistics.h"
#include "chrome/browser/profiles/profile_statistics_factory.h"
#include "chrome/browser/profiles/profile_window.h"
#include "chrome/browser/profiles/profiles_state.h"
#include "chrome/browser/signin/signin_features.h"
#include "chrome/browser/signin/signin_util.h"
#include "chrome/browser/themes/theme_properties.h"
#include "chrome/browser/themes/theme_service.h"
#include "chrome/browser/themes/theme_service_factory.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/chrome_pages.h"
#include "chrome/browser/ui/profile_picker.h"
#include "chrome/browser/ui/signin/profile_colors_util.h"
#include "chrome/browser/ui/singleton_tabs.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/browser/ui/webui/profile_helper.h"
#include "chrome/browser/ui/webui/signin/login_ui_service.h"
#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
#include "chrome/browser/ui/webui/theme_source.h"
#include "chrome/browser/ui/webui/webui_util.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/themes/autogenerated_theme_util.h"
#include "chrome/common/webui_url_constants.h"
#include "components/signin/public/identity_manager/account_info.h"
#include "components/startup_metric_utils/browser/startup_metric_utils.h"
#include "content/public/browser/url_data_source.h"
#include "content/public/browser/web_ui.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/webui/web_ui_util.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/image/image.h"

#if BUILDFLAG(IS_CHROMEOS_LACROS)
#include "chrome/browser/lacros/account_manager/account_manager_util.h"
#include "chrome/browser/lacros/account_manager/account_profile_mapper.h"
#include "chrome/browser/lacros/lacros_url_handling.h"
#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
#include "chrome/browser/ui/webui/signin/profile_picker_lacros_sign_in_provider.h"
#include "components/account_manager_core/account.h"
#endif

namespace {
const size_t kProfileCardAvatarSize = 74;
const size_t kProfileCreationAvatarSize = 100;

constexpr int kDefaultThemeColorId = -1;
constexpr int kManuallyPickedColorId = 0;

// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class ProfilePickerAction {
  kLaunchExistingProfile = 0,
  kLaunchExistingProfileCustomizeSettings = 1,
  kLaunchGuestProfile = 2,
  kLaunchNewProfile = 3,
  kDeleteProfile = 4,
  kMaxValue = kDeleteProfile,
};

absl::optional<SkColor> GetChromeColorColorById(int color_id) {
  for (chrome_colors::ColorInfo color_info :
       chrome_colors::kGeneratedColorsInfo) {
    if (color_id == color_info.id)
      return color_info.color;
  }

  return absl::nullopt;
}

void RecordProfilePickerAction(ProfilePickerAction action) {
  base::UmaHistogramEnumeration("ProfilePicker.UserAction", action);
}

void RecordAskOnStartupChanged(bool value) {
  base::UmaHistogramBoolean("ProfilePicker.AskOnStartupChanged", value);
}

void RecordNewProfileSpec(absl::optional<SkColor> profile_color,
                          bool create_shortcut) {
  int theme_id =
      profile_color.has_value()
          ? chrome_colors::ChromeColorsService::GetColorId(*profile_color)
          : chrome_colors::kDefaultColorId;
  base::UmaHistogramSparse("ProfilePicker.NewProfileTheme", theme_id);

  if (ProfileShortcutManager::IsFeatureEnabled()) {
    base::UmaHistogramBoolean("ProfilePicker.NewProfileCreateShortcut",
                              create_shortcut);
  }
}

base::Value GetAutogeneratedProfileThemeInfoValue(int color_id,
                                                  absl::optional<SkColor> color,
                                                  SkColor frame_color,
                                                  SkColor active_tab_color,
                                                  SkColor frame_text_color,
                                                  float scale_factor) {
  base::Value dict(base::Value::Type::DICTIONARY);
  dict.SetIntKey("colorId", color_id);
  if (color.has_value())
    dict.SetIntKey("color", *color);
  dict.SetStringKey("themeFrameColor",
                    color_utils::SkColorToRgbaString(frame_color));
  dict.SetStringKey("themeShapeColor",
                    color_utils::SkColorToRgbaString(active_tab_color));
  dict.SetStringKey("themeFrameTextColor",
                    color_utils::SkColorToRgbaString(frame_text_color));
  gfx::Image icon = profiles::GetPlaceholderAvatarIconWithColors(
      /*fill_color=*/frame_color,
      /*stroke_color=*/GetAvatarStrokeColor(frame_color),
      kProfileCreationAvatarSize * scale_factor);
  dict.SetStringKey("themeGenericAvatar",
                    webui::GetBitmapDataUrl(icon.AsBitmap()));
  return dict;
}

#if !BUILDFLAG(IS_OHOS)
base::Value CreateDefaultProfileThemeInfo(float scale_factor, bool dark_mode) {
  SkColor frame_color = ThemeProperties::GetDefaultColor(
      ThemeProperties::COLOR_FRAME_ACTIVE, /*incognito=*/false, dark_mode);
  SkColor active_tab_color = ThemeProperties::GetDefaultColor(
      ThemeProperties::COLOR_TOOLBAR, /*incognito=*/false, dark_mode);
  SkColor frame_text_color = ThemeProperties::GetDefaultColor(
      ThemeProperties::COLOR_TAB_FOREGROUND_INACTIVE_FRAME_ACTIVE,
      /*incognito=*/false, dark_mode);
  return GetAutogeneratedProfileThemeInfoValue(
      kDefaultThemeColorId, absl::nullopt, frame_color, active_tab_color,
      frame_text_color, scale_factor);
}
#endif

base::Value CreateAutogeneratedProfileThemeInfo(int color_id,
                                                SkColor color,
                                                float scale_factor) {
  auto theme_colors = GetAutogeneratedThemeColors(color);
  SkColor frame_color = theme_colors.frame_color;
  SkColor active_tab_color = theme_colors.active_tab_color;
  SkColor frame_text_color = theme_colors.frame_text_color;
  return GetAutogeneratedProfileThemeInfoValue(color_id, color, frame_color,
                                               active_tab_color,
                                               frame_text_color, scale_factor);
}

void OpenOnSelectProfileTargetUrl(Browser* browser) {
  GURL target_page_url = ProfilePicker::GetOnSelectProfileTargetUrl();
  if (target_page_url.is_empty())
    return;

  if (target_page_url.spec() == chrome::kChromeUIHelpURL) {
    chrome::ShowAboutChrome(browser);
  } else if (target_page_url.spec() == chrome::kChromeUISettingsURL) {
    chrome::ShowSettings(browser);
  } else if (target_page_url.spec() == ProfilePicker::kTaskManagerUrl) {
    chrome::OpenTaskManager(browser);
  } else {
    NavigateParams params(
        GetSingletonTabNavigateParams(browser, target_page_url));
    params.path_behavior = NavigateParams::RESPECT;
    ShowSingletonTabOverwritingNTP(browser, &params);
  }
}

base::Value CreateProfileEntry(const ProfileAttributesEntry* entry,
                               int avatar_icon_size) {
  base::Value profile_entry(base::Value::Type::DICTIONARY);
  profile_entry.SetKey("profilePath", base::FilePathToValue(entry->GetPath()));
  profile_entry.SetStringKey("localProfileName", entry->GetLocalProfileName());
  profile_entry.SetBoolKey(
      "isSyncing", entry->GetSigninState() ==
                       SigninState::kSignedInWithConsentedPrimaryAccount);
  profile_entry.SetBoolKey("needsSignin", entry->IsSigninRequired());
  // GAIA full name/user name can be empty, if the profile is not signed in to
  // chrome.
  profile_entry.SetStringKey("gaiaName", entry->GetGAIAName());
  profile_entry.SetStringKey("userName", entry->GetUserName());
  profile_entry.SetBoolKey("isManaged",
                           AccountInfo::IsManaged(entry->GetHostedDomain()));
  gfx::Image icon =
      profiles::GetSizedAvatarIcon(entry->GetAvatarIcon(avatar_icon_size), true,
                                   avatar_icon_size, avatar_icon_size);
  std::string icon_url = webui::GetBitmapDataUrl(icon.AsBitmap());
  profile_entry.SetStringKey("avatarIcon", icon_url);
#if BUILDFLAG(IS_CHROMEOS_LACROS)
  profile_entry.SetBoolKey("isPrimaryLacrosProfile",
                           Profile::IsMainProfilePath(entry->GetPath()));
#else
  profile_entry.SetBoolKey("isPrimaryLacrosProfile", false);
#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
  return profile_entry;
}

#if BUILDFLAG(IS_CHROMEOS_LACROS)
base::FilePath GetCurrentProfilePath(content::WebUI* web_ui) {
  DCHECK(web_ui);
  return web_ui->GetWebContents()->GetBrowserContext()->GetPath();
}

bool IsSelectingSecondaryAccount(content::WebUI* web_ui) {
  // If this WebUI page is rendered in a user profile (and not the default
  // picker profile), this means the page should show accounts that are
  // available as secondary for this specific profile.
  return GetCurrentProfilePath(web_ui) != ProfilePicker::GetPickerProfilePath();
}

SkBitmap GetAvailableAccountBitmap(const gfx::Image& gaia_image,
                                   bool dark_mode) {
  if (!gaia_image.IsEmpty())
    return gaia_image.AsBitmap();

  // Return a default avatar.
  const int kAccountPictureSize = 128;
  ProfileThemeColors colors = GetDefaultProfileThemeColors(dark_mode);
  gfx::Image default_image = profiles::GetPlaceholderAvatarIconWithColors(
      colors.default_avatar_fill_color, colors.default_avatar_stroke_color,
      kAccountPictureSize);
  return default_image.AsBitmap();
}
#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)

void RecordProfilingFinishReason(
    metrics::StartupProfilingFinishReason finish_reason) {
  base::UmaHistogramEnumeration(
      "ProfilePicker.FirstProfileTime.FirstWebContentsFinishReason",
      finish_reason);
}

class FirstWebContentsProfilerForProfilePicker
    : public metrics::FirstWebContentsProfilerBase {
 public:
  explicit FirstWebContentsProfilerForProfilePicker(
      content::WebContents* web_contents,
      base::TimeTicks pick_time);

  FirstWebContentsProfilerForProfilePicker(
      const FirstWebContentsProfilerForProfilePicker&) = delete;
  FirstWebContentsProfilerForProfilePicker& operator=(
      const FirstWebContentsProfilerForProfilePicker&) = delete;

 protected:
  // FirstWebContentsProfilerBase:
  void RecordFinishReason(
      metrics::StartupProfilingFinishReason finish_reason) override;
  void RecordNavigationFinished(base::TimeTicks navigation_start) override;
  void RecordFirstNonEmptyPaint() override;
  bool WasStartupInterrupted() override;

 private:
  ~FirstWebContentsProfilerForProfilePicker() override;

  const base::TimeTicks pick_time_;
};

FirstWebContentsProfilerForProfilePicker::
    FirstWebContentsProfilerForProfilePicker(content::WebContents* web_contents,
                                             base::TimeTicks pick_time)
    : FirstWebContentsProfilerBase(web_contents), pick_time_(pick_time) {
  DCHECK(!pick_time_.is_null());
}

FirstWebContentsProfilerForProfilePicker::
    ~FirstWebContentsProfilerForProfilePicker() = default;

void FirstWebContentsProfilerForProfilePicker::RecordFinishReason(
    metrics::StartupProfilingFinishReason finish_reason) {
  RecordProfilingFinishReason(finish_reason);
}

void FirstWebContentsProfilerForProfilePicker::RecordNavigationFinished(
    base::TimeTicks navigation_start) {
  // Nothing to record here for Profile Picker startups.
}

void FirstWebContentsProfilerForProfilePicker::RecordFirstNonEmptyPaint() {
  const char histogram_name[] =
      "ProfilePicker.FirstProfileTime.FirstWebContentsNonEmptyPaint";
  base::TimeTicks paint_time = base::TimeTicks::Now();
  base::UmaHistogramLongTimes100(histogram_name, paint_time - pick_time_);
  TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0("startup", histogram_name,
                                                   this, pick_time_);
  TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0("startup", histogram_name,
                                                 this, paint_time);
}

bool FirstWebContentsProfilerForProfilePicker::WasStartupInterrupted() {
  // We're assuming that no interruptions block opening an existing profile
  // from the profile picker. We would detect this by observing really high
  // latency on the tracked metric, and can start tracking interruptions if we
  // find that such cases occur.
  return false;
}

void BeginFirstWebContentsProfiling(Browser* browser,
                                    base::TimeTicks pick_time) {
  content::WebContents* visible_contents =
      metrics::FirstWebContentsProfilerBase::GetVisibleContents(browser);
  if (!visible_contents) {
    RecordProfilingFinishReason(metrics::StartupProfilingFinishReason::
                                    kAbandonNoInitiallyVisibleContent);
    return;
  }

  if (visible_contents->CompletedFirstVisuallyNonEmptyPaint()) {
    RecordProfilingFinishReason(
        metrics::StartupProfilingFinishReason::kAbandonAlreadyPaintedContent);
    return;
  }

  // FirstWebContentsProfilerForProfilePicker owns itself and is also bound to
  // |visible_contents|'s lifetime by observing WebContentsDestroyed().
  new FirstWebContentsProfilerForProfilePicker(visible_contents, pick_time);
}

}  // namespace

ProfilePickerHandler::ProfilePickerHandler() = default;

ProfilePickerHandler::~ProfilePickerHandler() {
  OnJavascriptDisallowed();
}

void ProfilePickerHandler::EnableStartupMetrics() {
  DCHECK(creation_time_on_startup_.is_null());
  content::WebContents* contents = web_ui()->GetWebContents();
  if (contents->GetVisibility() == content::Visibility::VISIBLE) {
    // Only record paint event if the window is visible.
    creation_time_on_startup_ = base::TimeTicks::Now();
    Observe(web_ui()->GetWebContents());
  }
}

void ProfilePickerHandler::RegisterMessages() {
  web_ui()->RegisterDeprecatedMessageCallback(
      "mainViewInitialize",
      base::BindRepeating(&ProfilePickerHandler::HandleMainViewInitialize,
                          base::Unretained(this)));
  web_ui()->RegisterDeprecatedMessageCallback(
      "launchSelectedProfile",
      base::BindRepeating(&ProfilePickerHandler::HandleLaunchSelectedProfile,
                          base::Unretained(this), /*open_settings=*/false));
  web_ui()->RegisterDeprecatedMessageCallback(
      "openManageProfileSettingsSubPage",
      base::BindRepeating(&ProfilePickerHandler::HandleLaunchSelectedProfile,
                          base::Unretained(this), /*open_settings=*/true));
  web_ui()->RegisterDeprecatedMessageCallback(
      "launchGuestProfile",
      base::BindRepeating(&ProfilePickerHandler::HandleLaunchGuestProfile,
                          base::Unretained(this)));
  web_ui()->RegisterDeprecatedMessageCallback(
      "askOnStartupChanged",
      base::BindRepeating(&ProfilePickerHandler::HandleAskOnStartupChanged,
                          base::Unretained(this)));
  web_ui()->RegisterDeprecatedMessageCallback(
      "getNewProfileSuggestedThemeInfo",
      base::BindRepeating(
          &ProfilePickerHandler::HandleGetNewProfileSuggestedThemeInfo,
          base::Unretained(this)));
  web_ui()->RegisterDeprecatedMessageCallback(
      "getProfileThemeInfo",
      base::BindRepeating(&ProfilePickerHandler::HandleGetProfileThemeInfo,
                          base::Unretained(this)));
  web_ui()->RegisterDeprecatedMessageCallback(
      "removeProfile",
      base::BindRepeating(&ProfilePickerHandler::HandleRemoveProfile,
                          base::Unretained(this)));
  web_ui()->RegisterDeprecatedMessageCallback(
      "getProfileStatistics",
      base::BindRepeating(&ProfilePickerHandler::HandleGetProfileStatistics,
                          base::Unretained(this)));
  web_ui()->RegisterDeprecatedMessageCallback(
      "loadSignInProfileCreationFlow",
      base::BindRepeating(
          &ProfilePickerHandler::HandleLoadSignInProfileCreationFlow,
          base::Unretained(this)));
  // TODO(crbug.com/1115056): Consider renaming this message to
  // 'createLocalProfile' as this is only used for local profiles.
  web_ui()->RegisterDeprecatedMessageCallback(
      "getAvailableIcons",
      base::BindRepeating(&ProfilePickerHandler::HandleGetAvailableIcons,
                          base::Unretained(this)));
  web_ui()->RegisterDeprecatedMessageCallback(
      "createProfile",
      base::BindRepeating(&ProfilePickerHandler::HandleCreateProfile,
                          base::Unretained(this)));
  web_ui()->RegisterDeprecatedMessageCallback(
      "getSwitchProfile",
      base::BindRepeating(&ProfilePickerHandler::HandleGetSwitchProfile,
                          base::Unretained(this)));
  web_ui()->RegisterDeprecatedMessageCallback(
      "confirmProfileSwitch",
      base::BindRepeating(&ProfilePickerHandler::HandleConfirmProfileSwitch,
                          base::Unretained(this)));
  web_ui()->RegisterDeprecatedMessageCallback(
      "cancelProfileSwitch",
      base::BindRepeating(&ProfilePickerHandler::HandleCancelProfileSwitch,
                          base::Unretained(this)));
  web_ui()->RegisterDeprecatedMessageCallback(
      "setProfileName",
      base::BindRepeating(&ProfilePickerHandler::HandleSetProfileName,
                          base::Unretained(this)));
  web_ui()->RegisterDeprecatedMessageCallback(
      "recordSignInPromoImpression",
      base::BindRepeating(
          &ProfilePickerHandler::HandleRecordSignInPromoImpression,
          base::Unretained(this)));
#if BUILDFLAG(IS_CHROMEOS_LACROS)
  web_ui()->RegisterDeprecatedMessageCallback(
      "getAvailableAccounts",
      base::BindRepeating(&ProfilePickerHandler::HandleGetAvailableAccounts,
                          base::Unretained(this)));
  web_ui()->RegisterMessageCallback(
      "openAshAccountSettingsPage",
      base::BindRepeating(
          &ProfilePickerHandler::HandleOpenAshAccountSettingsPage,
          base::Unretained(this)));
#endif
  Profile* profile = Profile::FromWebUI(web_ui());
  content::URLDataSource::Add(profile, std::make_unique<ThemeSource>(profile));
}

void ProfilePickerHandler::OnJavascriptAllowed() {
  ProfileManager* profile_manager = g_browser_process->profile_manager();
#if BUILDFLAG(IS_CHROMEOS_LACROS)
  account_profile_mapper_observation_.Observe(
      profile_manager->GetAccountProfileMapper());
#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
  profile_attributes_storage_observation_.Observe(
      &profile_manager->GetProfileAttributesStorage());
}
void ProfilePickerHandler::OnJavascriptDisallowed() {
#if BUILDFLAG(IS_CHROMEOS_LACROS)
  account_profile_mapper_observation_.Reset();
#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
  profile_attributes_storage_observation_.Reset();
  weak_factory_.InvalidateWeakPtrs();
}

void ProfilePickerHandler::HandleMainViewInitialize(
    const base::ListValue* args) {
  if (!creation_time_on_startup_.is_null() && !main_view_initialized_) {
    // This function can be called multiple times if the page is reloaded. The
    // histogram is only recorded once.
    main_view_initialized_ = true;
    base::UmaHistogramTimes("ProfilePicker.StartupTime.MainViewInitialized",
                            base::TimeTicks::Now() - creation_time_on_startup_);
  }

  AllowJavascript();
  PushProfilesList();
}

void ProfilePickerHandler::HandleLaunchSelectedProfile(
    bool open_settings,
    const base::ListValue* args) {
  TRACE_EVENT1("browser", "ProfilePickerHandler::HandleLaunchSelectedProfile",
               "args", args->DebugString());
  if (args->GetList().empty())
    return;
  const base::Value& profile_path_value = args->GetList()[0];

  absl::optional<base::FilePath> profile_path =
      base::ValueToFilePath(profile_path_value);
  if (!profile_path)
    return;

  ProfileAttributesEntry* entry =
      g_browser_process->profile_manager()
          ->GetProfileAttributesStorage()
          .GetProfileAttributesWithPath(*profile_path);
  if (!entry) {
    NOTREACHED();
    return;
  }

  if (entry->IsSigninRequired()) {
    DCHECK(signin_util::IsForceSigninEnabled());
    if (entry->CanBeManaged() &&
        base::FeatureList::IsEnabled(features::kForceSignInReauth)) {
      ProfilePickerForceSigninDialog::ShowReauthDialog(
          web_ui()->GetWebContents()->GetBrowserContext(),
          base::UTF16ToUTF8(entry->GetUserName()), *profile_path);
    } else if (entry->GetActiveTime() != base::Time()) {
      // If force-sign-in is enabled, do not allow users to sign in to a
      // pre-existing locked profile, as this may force unexpected profile data
      // merge. We consider a profile as pre-existing if it has been actived
      // previously. A pre-existed profile can still be used if it has been
      // signed in with an email address matched RestrictSigninToPattern policy
      // already.
      LoginUIServiceFactory::GetForProfile(
          Profile::FromWebUI(web_ui())->GetOriginalProfile())
          ->SetProfileBlockingErrorMessage();
      ProfilePickerForceSigninDialog::ShowDialogAndDisplayErrorMessage(
          web_ui()->GetWebContents()->GetBrowserContext());
    } else {
      // Fresh sign in via profile picker without existing email address.
      ProfilePickerForceSigninDialog::ShowForceSigninDialog(
          web_ui()->GetWebContents()->GetBrowserContext(), *profile_path);
    }
    return;
  }

#if BUILDFLAG(IS_CHROMEOS_LACROS)
  if (!profiles::AreSecondaryProfilesAllowed()) {
    if (!Profile::IsMainProfilePath(*profile_path)) {
      LoginUIServiceFactory::GetForProfile(
          Profile::FromWebUI(web_ui())->GetOriginalProfile())
          ->SetProfileBlockingErrorMessage();
      ProfilePickerForceSigninDialog::ShowDialogAndDisplayErrorMessage(
          web_ui()->GetWebContents()->GetBrowserContext());
      return;
    }
  }
#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)

  if (!creation_time_on_startup_.is_null() &&
      // Avoid overriding the picked time if already recorded. This can happen
      // for example if multiple profiles are picked: https://crbug.com/1277466.
      profile_picked_time_on_startup_.is_null()) {
    profile_picked_time_on_startup_ = base::TimeTicks::Now();
  }
  profiles::SwitchToProfile(
      *profile_path, /*always_create=*/false,
      base::BindOnce(&ProfilePickerHandler::OnSwitchToProfileComplete,
                     weak_factory_.GetWeakPtr(), false, open_settings));
}

void ProfilePickerHandler::HandleLaunchGuestProfile(
    const base::ListValue* args) {
  // TODO(crbug.com/1063856): Add check |IsGuestModeEnabled| once policy
  // checking has been added to the UI.
  profiles::SwitchToGuestProfile(
      base::BindOnce(&ProfilePickerHandler::OnSwitchToProfileComplete,
                     weak_factory_.GetWeakPtr(), false, false));
}

void ProfilePickerHandler::HandleAskOnStartupChanged(
    const base::ListValue* args) {
  const auto& list = args->GetList();
  if (list.empty() || !list[0].is_bool())
    return;
  const bool show_on_startup = list[0].GetBool();

  PrefService* prefs = g_browser_process->local_state();
  prefs->SetBoolean(prefs::kBrowserShowProfilePickerOnStartup, show_on_startup);
  RecordAskOnStartupChanged(show_on_startup);
}

void ProfilePickerHandler::HandleGetNewProfileSuggestedThemeInfo(
    const base::ListValue* args) {
  AllowJavascript();
  CHECK_EQ(1U, args->GetList().size());
  const base::Value& callback_id = args->GetList()[0];

#if BUILDFLAG(IS_CHROMEOS_LACROS)
  if (IsSelectingSecondaryAccount(web_ui())) {
    // The picker offers secondary accounts for a given existing profile.
    // Extract the color from the current profile (where this is rendered).
    ThemeService* theme_service =
        ThemeServiceFactory::GetForProfile(Profile::FromBrowserContext(
            web_ui()->GetWebContents()->GetBrowserContext()));
    base::Value profile_dict;
    if (theme_service->UsingAutogeneratedTheme()) {
      // We'll never use `profile_dict` for showing the color picker so we can
      // pass in kManuallyPickedColorId to simplify the code.
      profile_dict = CreateAutogeneratedProfileThemeInfo(
          kManuallyPickedColorId, theme_service->GetAutogeneratedThemeColor(),
          web_ui()->GetDeviceScaleFactor());
    } else {
      profile_dict = CreateDefaultProfileThemeInfo(
          web_ui()->GetDeviceScaleFactor(),
          webui::GetNativeTheme(web_ui()->GetWebContents())
              ->ShouldUseDarkColors());
    }
    ResolveJavascriptCallback(callback_id, std::move(profile_dict));
    return;
  }
#endif
  chrome_colors::ColorInfo color_info = GenerateNewProfileColor();
  base::Value dict = CreateAutogeneratedProfileThemeInfo(
      color_info.id, color_info.color, web_ui()->GetDeviceScaleFactor());
  ResolveJavascriptCallback(callback_id, std::move(dict));
}

void ProfilePickerHandler::HandleGetProfileThemeInfo(
    const base::ListValue* args) {
  AllowJavascript();
  CHECK_EQ(2U, args->GetList().size());
  const base::Value& callback_id = args->GetList()[0];
  const base::Value& user_theme_choice = args->GetList()[1];
  int color_id = user_theme_choice.FindIntKey("colorId").value();
  absl::optional<SkColor> color = user_theme_choice.FindDoubleKey("color");
  base::Value dict;
  switch (color_id) {
    case kDefaultThemeColorId:
#if !BUILDFLAG(IS_OHOS)
      dict = CreateDefaultProfileThemeInfo(
          web_ui()->GetDeviceScaleFactor(),
          webui::GetNativeTheme(web_ui()->GetWebContents())
              ->ShouldUseDarkColors());
#else
      LOG(INFO) << "ProfilePickerHandler::HandleGetProfileThemeInfo "
                   "kDefaultThemeColorId TODO for OS_OHOS";
#endif
      break;
    case kManuallyPickedColorId:
      dict = CreateAutogeneratedProfileThemeInfo(
          color_id, *color, web_ui()->GetDeviceScaleFactor());
      break;
    default:
      dict = CreateAutogeneratedProfileThemeInfo(
          color_id, *GetChromeColorColorById(color_id),
          web_ui()->GetDeviceScaleFactor());
      break;
  }
  ResolveJavascriptCallback(callback_id, std::move(dict));
}

void ProfilePickerHandler::HandleGetAvailableIcons(
    const base::ListValue* args) {
  AllowJavascript();
  CHECK_EQ(1U, args->GetList().size());
  const base::Value& callback_id = args->GetList()[0];
  ResolveJavascriptCallback(
      callback_id,
      base::Value(profiles::GetCustomProfileAvatarIconsAndLabels()));
}

void ProfilePickerHandler::HandleCreateProfile(const base::ListValue* args) {
  CHECK_EQ(4U, args->GetList().size());
  std::u16string profile_name =
      base::UTF8ToUTF16(args->GetList()[0].GetString());
  // profileColor is undefined for the default theme.
  absl::optional<SkColor> profile_color;
  if (args->GetList()[1].is_int())
    profile_color = args->GetList()[1].GetInt();
  size_t avatar_index = args->GetList()[2].GetInt();
  bool create_shortcut = args->GetList()[3].GetBool();
  base::TrimWhitespace(profile_name, base::TRIM_ALL, &profile_name);
  CHECK(!profile_name.empty());

#ifndef NDEBUG
  DCHECK(profiles::IsDefaultAvatarIconIndex(avatar_index));
#endif

  ProfileMetrics::LogProfileAddNewUser(
      ProfileMetrics::ADD_NEW_PROFILE_PICKER_LOCAL);
  ProfileManager::CreateMultiProfileAsync(
      profile_name, avatar_index, /*is_hidden=*/false,
      base::BindRepeating(&ProfilePickerHandler::OnProfileCreated,
                          weak_factory_.GetWeakPtr(), profile_color,
                          create_shortcut));
}

void ProfilePickerHandler::HandleGetSwitchProfile(const base::ListValue* args) {
  AllowJavascript();
  CHECK_EQ(1U, args->GetList().size());
  const base::Value& callback_id = args->GetList()[0];
  int avatar_icon_size =
      kProfileCardAvatarSize * web_ui()->GetDeviceScaleFactor();
  base::FilePath profile_path = ProfilePicker::GetSwitchProfilePath();
  ProfileAttributesEntry* entry =
      g_browser_process->profile_manager()
          ->GetProfileAttributesStorage()
          .GetProfileAttributesWithPath(profile_path);
  CHECK(entry);
  base::Value dict = CreateProfileEntry(entry, avatar_icon_size);
  ResolveJavascriptCallback(callback_id, std::move(dict));
}

void ProfilePickerHandler::HandleConfirmProfileSwitch(
    const base::ListValue* args) {
  if (args->GetList().empty())
    return;
  const base::Value& profile_path_value = args->GetList()[0];

  absl::optional<base::FilePath> profile_path =
      base::ValueToFilePath(profile_path_value);
  if (!profile_path)
    return;

  // TODO(https://crbug.com/1182206): remove the profile used for the sign-in
  // flow.
  profiles::SwitchToProfile(
      *profile_path, /*always_create=*/false,
      base::BindOnce(&ProfilePickerHandler::OnSwitchToProfileComplete,
                     weak_factory_.GetWeakPtr(), false, false));
}

void ProfilePickerHandler::HandleCancelProfileSwitch(
    const base::ListValue* args) {
  ProfilePicker::CancelSignedInFlow();
}

void ProfilePickerHandler::OnProfileCreated(
    absl::optional<SkColor> profile_color,
    bool create_shortcut,
    Profile* profile,
    Profile::CreateStatus status) {
  switch (status) {
    case Profile::CREATE_STATUS_LOCAL_FAIL: {
      NOTREACHED() << "Local fail in creating new profile";
      break;
    }

    case Profile::CREATE_STATUS_CREATED:
      // Do nothing for an intermediate status.
      return;

    case Profile::CREATE_STATUS_INITIALIZED: {
      OnProfileCreationSuccess(profile_color, create_shortcut, profile);
      break;
    }
  }

  FireWebUIListener("create-profile-finished", base::Value());
}

void ProfilePickerHandler::OnProfileCreationSuccess(
    absl::optional<SkColor> profile_color,
    bool create_shortcut,
    Profile* profile) {
  DCHECK(profile);
  DCHECK(!signin_util::IsForceSigninEnabled());

  // Apply a new color to the profile or use the default theme.
  auto* theme_service = ThemeServiceFactory::GetForProfile(profile);
  if (profile_color.has_value())
    theme_service->BuildAutogeneratedThemeFromColor(*profile_color);
  else
    theme_service->UseDefaultTheme();

  // Create shortcut if needed.
  if (create_shortcut) {
    DCHECK(ProfileShortcutManager::IsFeatureEnabled());
    ProfileShortcutManager* shortcut_manager =
        g_browser_process->profile_manager()->profile_shortcut_manager();
    DCHECK(shortcut_manager);
    if (shortcut_manager)
      shortcut_manager->CreateProfileShortcut(profile->GetPath());
  }

  // Skip the FRE for this profile as sign-in was offered as part of the flow.
  profile->GetPrefs()->SetBoolean(prefs::kHasSeenWelcomePage, true);

  RecordNewProfileSpec(profile_color, create_shortcut);
  // Launch profile and close the picker.
  profiles::OpenBrowserWindowForProfile(
      base::BindOnce(&ProfilePickerHandler::OnSwitchToProfileComplete,
                     weak_factory_.GetWeakPtr(), true, false),
      false,  // Don't create a window if one already exists.
      true,   // Create a first run window.
      false,  // There is no need to unblock all extensions because we only open
              // browser window if the Profile is not locked. Hence there is no
              // extension blocked.
      profile);
}

void ProfilePickerHandler::HandleRecordSignInPromoImpression(
    const base::ListValue* /*args*/) {
  signin_metrics::RecordSigninImpressionUserActionForAccessPoint(
      signin_metrics::AccessPoint::ACCESS_POINT_USER_MANAGER);
}

void ProfilePickerHandler::HandleSetProfileName(const base::ListValue* args) {
  CHECK_EQ(2U, args->GetList().size());
  const base::Value& profile_path_value = args->GetList()[0];
  absl::optional<base::FilePath> profile_path =
      base::ValueToFilePath(profile_path_value);

  if (!profile_path) {
    NOTREACHED();
    return;
  }
  std::u16string profile_name =
      base::UTF8ToUTF16(args->GetList()[1].GetString());
  base::TrimWhitespace(profile_name, base::TRIM_ALL, &profile_name);
  CHECK(!profile_name.empty());
  ProfileAttributesEntry* entry =
      g_browser_process->profile_manager()
          ->GetProfileAttributesStorage()
          .GetProfileAttributesWithPath(profile_path.value());
  CHECK(entry);
  entry->SetLocalProfileName(profile_name, /*is_default_name=*/false);
}

void ProfilePickerHandler::HandleRemoveProfile(const base::ListValue* args) {
  CHECK_EQ(1U, args->GetList().size());
  const base::Value& profile_path_value = args->GetList()[0];
  absl::optional<base::FilePath> profile_path =
      base::ValueToFilePath(profile_path_value);

  if (!profile_path) {
    NOTREACHED();
    return;
  }

#if BUILDFLAG(IS_CHROMEOS_LACROS)
  // On Lacros, the primary profile should never be deleted.
  CHECK(!Profile::IsMainProfilePath(*profile_path));
#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)

  RecordProfilePickerAction(ProfilePickerAction::kDeleteProfile);
  webui::DeleteProfileAtPath(*profile_path,
                             ProfileMetrics::DELETE_PROFILE_USER_MANAGER);
}

void ProfilePickerHandler::HandleGetProfileStatistics(
    const base::ListValue* args) {
  AllowJavascript();
  CHECK_EQ(1U, args->GetList().size());
  const base::Value& profile_path_value = args->GetList()[0];
  absl::optional<base::FilePath> profile_path =
      base::ValueToFilePath(profile_path_value);
  if (!profile_path)
    return;

  Profile* profile =
      g_browser_process->profile_manager()->GetProfileByPath(*profile_path);

  if (profile) {
    GatherProfileStatistics(profile);
  } else {
    g_browser_process->profile_manager()->LoadProfileByPath(
        *profile_path, false,
        base::BindOnce(&ProfilePickerHandler::GatherProfileStatistics,
                       weak_factory_.GetWeakPtr()));
  }
}

void ProfilePickerHandler::GatherProfileStatistics(Profile* profile) {
  if (!profile) {
    return;
  }

  ProfileStatisticsFactory::GetForProfile(profile)->GatherStatistics(
      base::BindRepeating(&ProfilePickerHandler::OnProfileStatisticsReceived,
                          weak_factory_.GetWeakPtr(), profile->GetPath()));
}

void ProfilePickerHandler::OnProfileStatisticsReceived(
    const base::FilePath& profile_path,
    profiles::ProfileCategoryStats result) {
  base::Value dict(base::Value::Type::DICTIONARY);
  dict.SetKey("profilePath", base::FilePathToValue(profile_path));
  base::Value stats(base::Value::Type::DICTIONARY);
  // Categories are defined in |kProfileStatisticsCategories|
  // {"BrowsingHistory", "Passwords", "Bookmarks", "Autofill"}.
  for (const auto& item : result) {
    stats.SetIntKey(item.category, item.count);
  }
  dict.SetKey("statistics", std::move(stats));
  FireWebUIListener("profile-statistics-received", std::move(dict));
}

void ProfilePickerHandler::HandleLoadSignInProfileCreationFlow(
    const base::ListValue* args) {
  AllowJavascript();
  CHECK_EQ(2U, args->GetList().size());
  absl::optional<SkColor> profile_color = args->GetList()[0].GetIfInt();
  const std::string& gaia_id = args->GetList()[1].GetString();

#if BUILDFLAG(IS_CHROMEOS_LACROS)
  if (IsSelectingSecondaryAccount(web_ui())) {
    AccountProfileMapper* mapper =
        g_browser_process->profile_manager()->GetAccountProfileMapper();
    if (gaia_id.empty()) {
      mapper->ShowAddAccountDialog(GetCurrentProfilePath(web_ui()),
                                   account_manager::AccountManagerFacade::
                                       AccountAdditionSource::kOgbAddAccount,
                                   AccountProfileMapper::AddAccountCallback());
    } else {
      mapper->AddAccount(GetCurrentProfilePath(web_ui()),
                         account_manager::AccountKey(
                             gaia_id, account_manager::AccountType::kGaia),
                         AccountProfileMapper::AddAccountCallback());
    }
    ProfilePicker::Hide();
    return;
  }

  DCHECK(!lacros_sign_in_provider_);
  lacros_sign_in_provider_ =
      std::make_unique<ProfilePickerLacrosSignInProvider>();
  ProfilePickerLacrosSignInProvider::SignedInCallback callback =
      base::BindOnce(&ProfilePickerHandler::OnLacrosSignedInProfileCreated,
                     weak_factory_.GetWeakPtr(), profile_color);
  if (gaia_id.empty()) {
    lacros_sign_in_provider_->ShowAddAccountDialogAndCreateSignedInProfile(
        std::move(callback));
  } else {
    lacros_sign_in_provider_->CreateSignedInProfileWithExistingAccount(
        gaia_id, std::move(callback));
  }
#elif BUILDFLAG(ENABLE_DICE_SUPPORT)
  DCHECK(gaia_id.empty()) << "gaiaId is only supported on Lacros.";
  if (signin_util::IsForceSigninEnabled()) {
    // Force sign-in policy uses a separate flow that doesn't initialize the
    // profile color. Generate a new profile color here.
    profile_color = GenerateNewProfileColor().color;
  }
  ProfilePicker::SwitchToDiceSignIn(
      profile_color, base::BindOnce(&ProfilePickerHandler::OnLoadSigninFinished,
                                    weak_factory_.GetWeakPtr()));
#else
  NOTREACHED();
#endif
}

void ProfilePickerHandler::OnLoadSigninFinished(bool success) {
  FireWebUIListener("load-signin-finished", base::Value(success));
}

void ProfilePickerHandler::OnSwitchToProfileComplete(bool new_profile,
                                                     bool open_settings,
                                                     Profile* profile) {
  TRACE_EVENT1("browser", "ProfilePickerHandler::OnSwitchToProfileComplete",
               "profile_path", profile->GetPath().AsUTF8Unsafe());
  Browser* browser = chrome::FindAnyBrowser(profile, false);
  DCHECK(browser);
  DCHECK(browser->window());

  // Measure startup time to display first web contents if the profile picker
  // was displayed on startup and if the initiating action is instrumented. For
  // example we don't record pick time for profile creations.
  if (!profile_picked_time_on_startup_.is_null()) {
    BeginFirstWebContentsProfiling(browser, profile_picked_time_on_startup_);
  }

  // Only show the profile switch IPH when the user clicked the card, and there
  // are multiple profiles.
  std::vector<ProfileAttributesEntry*> entries =
      g_browser_process->profile_manager()
          ->GetProfileAttributesStorage()
          .GetAllProfilesAttributes();
  int profile_count = std::count_if(
      entries.begin(), entries.end(),
      [](ProfileAttributesEntry* entry) { return !entry->IsOmitted(); });
  if (profile_count > 1 && !open_settings &&
      ProfilePicker::GetOnSelectProfileTargetUrl().is_empty()) {
    browser->window()->MaybeShowProfileSwitchIPH();
  }

  if (new_profile) {
    RecordProfilePickerAction(ProfilePickerAction::kLaunchNewProfile);
    ProfilePicker::Hide();
    return;
  }

  if (profile->IsGuestSession()) {
    RecordProfilePickerAction(ProfilePickerAction::kLaunchGuestProfile);
  } else {
    RecordProfilePickerAction(
        open_settings
            ? ProfilePickerAction::kLaunchExistingProfileCustomizeSettings
            : ProfilePickerAction::kLaunchExistingProfile);
  }

  if (open_settings) {
    // User clicked 'Edit' from the profile card menu.
    chrome::ShowSettingsSubPage(browser, chrome::kManageProfileSubPage);
  } else {
    // Opens a target url upon user selecting a pre-existing profile. For
    // new profiles the chrome welcome page is displayed.
    OpenOnSelectProfileTargetUrl(browser);
  }

  ProfilePicker::Hide();
}

void ProfilePickerHandler::PushProfilesList() {
  DCHECK(IsJavascriptAllowed());
  FireWebUIListener("profiles-list-changed", GetProfilesList());
}

void ProfilePickerHandler::SetProfilesOrder(
    const std::vector<ProfileAttributesEntry*>& entries) {
  profiles_order_.clear();
  size_t index = 0;
  for (const ProfileAttributesEntry* entry : entries) {
    profiles_order_[entry->GetPath()] = index++;
  }
}

std::vector<ProfileAttributesEntry*>
ProfilePickerHandler::GetProfileAttributes() {
  std::vector<ProfileAttributesEntry*> ordered_entries =
      g_browser_process->profile_manager()
          ->GetProfileAttributesStorage()
          .GetAllProfilesAttributesSortedByLocalProfilName();
  base::EraseIf(ordered_entries, [](const ProfileAttributesEntry* entry) {
    return entry->IsOmitted();
  });
  size_t number_of_profiles = ordered_entries.size();

  if (profiles_order_.size() != number_of_profiles) {
    // Should only happen the first time the function is called.
    // Profile creation and deletion are handled at
    // 'OnProfileAdded', 'OnProfileWasRemoved'.
    DCHECK(!profiles_order_.size());
    SetProfilesOrder(ordered_entries);
    return ordered_entries;
  }

  // Vector of nullptr entries.
  std::vector<ProfileAttributesEntry*> entries(number_of_profiles);
  for (ProfileAttributesEntry* entry : ordered_entries) {
    DCHECK(profiles_order_.find(entry->GetPath()) != profiles_order_.end());
    size_t index = profiles_order_[entry->GetPath()];
    DCHECK_LT(index, number_of_profiles);
    DCHECK(!entries[index]);
    entries[index] = entry;
  }
  return entries;
}

base::Value ProfilePickerHandler::GetProfilesList() {
  base::Value profiles_list(base::Value::Type::LIST);
  std::vector<ProfileAttributesEntry*> entries = GetProfileAttributes();
  const int avatar_icon_size =
      kProfileCardAvatarSize * web_ui()->GetDeviceScaleFactor();
  for (const ProfileAttributesEntry* entry : entries) {
    profiles_list.Append(CreateProfileEntry(entry, avatar_icon_size));
  }
  return profiles_list;
}

void ProfilePickerHandler::AddProfileToList(
    const base::FilePath& profile_path) {
  size_t number_of_profiles = profiles_order_.size();
  auto it_and_whether_inserted =
      profiles_order_.insert({profile_path, number_of_profiles});
  // We shouldn't add the same profile to the list more than once. Use
  // `insert()` to not corrput the map in case this happens.
  // https://crbug.com/1195784
  DCHECK(it_and_whether_inserted.second);
}

bool ProfilePickerHandler::RemoveProfileFromList(
    const base::FilePath& profile_path) {
  auto remove_it = profiles_order_.find(profile_path);
  // Guest and omitted profiles aren't added to the list.
  // It's possible that a profile gets marked as guest or as omitted after it
  // had been added to the list. In that case, the profile gets removed from the
  // list once in `OnProfileIsOmittedChanged()` but not the second time when
  // `OnProfileWasRemoved()` is called.
  if (remove_it == profiles_order_.end())
    return false;

  size_t index = remove_it->second;
  profiles_order_.erase(remove_it);
  for (auto& it : profiles_order_) {
    if (it.second > index)
      --it.second;
  }
  return true;
}

void ProfilePickerHandler::OnProfileAdded(const base::FilePath& profile_path) {
  ProfileAttributesEntry* entry =
      g_browser_process->profile_manager()
          ->GetProfileAttributesStorage()
          .GetProfileAttributesWithPath(profile_path);
  CHECK(entry);
  if (entry->IsOmitted())
    return;
  AddProfileToList(profile_path);
  PushProfilesList();
}

void ProfilePickerHandler::OnProfileWasRemoved(
    const base::FilePath& profile_path,
    const std::u16string& profile_name) {
  DCHECK(IsJavascriptAllowed());
  if (RemoveProfileFromList(profile_path))
    FireWebUIListener("profile-removed", base::FilePathToValue(profile_path));
}

void ProfilePickerHandler::OnProfileIsOmittedChanged(
    const base::FilePath& profile_path) {
  ProfileAttributesEntry* entry =
      g_browser_process->profile_manager()
          ->GetProfileAttributesStorage()
          .GetProfileAttributesWithPath(profile_path);
  CHECK(entry);
  if (entry->IsOmitted()) {
    if (RemoveProfileFromList(profile_path))
      PushProfilesList();
  } else {
    AddProfileToList(profile_path);
    PushProfilesList();
  }
}

void ProfilePickerHandler::OnProfileAvatarChanged(
    const base::FilePath& profile_path) {
  PushProfilesList();
}

void ProfilePickerHandler::OnProfileHighResAvatarLoaded(
    const base::FilePath& profile_path) {
  PushProfilesList();
}

void ProfilePickerHandler::OnProfileNameChanged(
    const base::FilePath& profile_path,
    const std::u16string& old_profile_name) {
  PushProfilesList();
}

void ProfilePickerHandler::OnProfileHostedDomainChanged(
    const base::FilePath& profile_path) {
  PushProfilesList();
}

void ProfilePickerHandler::DidFirstVisuallyNonEmptyPaint() {
  DCHECK(!creation_time_on_startup_.is_null());
  auto now = base::TimeTicks::Now();
  base::UmaHistogramTimes("ProfilePicker.StartupTime.FirstPaint",
                          now - creation_time_on_startup_);
  startup_metric_utils::RecordExternalStartupMetric(
      "ProfilePicker.StartupTime.FirstPaint.FromApplicationStart", now,
      /*set_non_browser_ui_displayed=*/true);
  // Stop observing so that the histogram is only recorded once.
  Observe(nullptr);
}

void ProfilePickerHandler::OnVisibilityChanged(content::Visibility visibility) {
  // If the profile picker is hidden, the first paint will be delayed until the
  // picker is visible again. Stop monitoring the first paint to avoid polluting
  // the metrics.
  if (visibility != content::Visibility::VISIBLE)
    Observe(nullptr);
}

#if BUILDFLAG(IS_CHROMEOS_LACROS)

void ProfilePickerHandler::HandleOpenAshAccountSettingsPage(
    base::Value::ConstListView args) {
  std::string settings_url = chrome::kChromeUIOSSettingsURL;
  settings_url.append(chromeos::settings::mojom::kMyAccountsSubpagePath);
  lacros_url_handling::NavigateInAsh(GURL(settings_url));
}

void ProfilePickerHandler::HandleGetAvailableAccounts(
    const base::ListValue* args) {
  AllowJavascript();
  UpdateAvailableAccounts();
}

void ProfilePickerHandler::UpdateAvailableAccounts() {
  AccountProfileMapper* mapper =
      g_browser_process->profile_manager()->GetAccountProfileMapper();

  if (IsSelectingSecondaryAccount(web_ui())) {
    GetAccountsAvailableAsSecondary(
        mapper, GetCurrentProfilePath(web_ui()),
        base::BindOnce(&ProfilePickerHandler::GetAvailableAccountsInfo,
                       weak_factory_.GetWeakPtr()));
    return;
  }
  GetAccountsAvailableAsPrimary(
      mapper,
      &g_browser_process->profile_manager()->GetProfileAttributesStorage(),
      base::BindOnce(&ProfilePickerHandler::GetAvailableAccountsInfo,
                     weak_factory_.GetWeakPtr()));
}

void ProfilePickerHandler::GetAvailableAccountsInfo(
    const std::vector<account_manager::Account>& accounts) {
  // If there's a request in flight, it deletes the current helper and starts a
  // new request.
  lacros_account_info_helper_ = std::make_unique<GetAccountInformationHelper>();

  std::vector<std::string> gaia_ids;
  for (const account_manager::Account& account : accounts)
    gaia_ids.push_back(account.key.id());
  lacros_account_info_helper_->Start(
      gaia_ids, base::BindOnce(&ProfilePickerHandler::SendAvailableAccounts,
                               weak_factory_.GetWeakPtr()));
}

void ProfilePickerHandler::SendAvailableAccounts(
    std::vector<GetAccountInformationHelper::GetAccountInformationResult>
        accounts) {
  base::Value accounts_list(base::Value::Type::LIST);
  for (const GetAccountInformationHelper::GetAccountInformationResult& account :
       accounts) {
    // TODO(https://crbug/1226050): Filter out items with no email as items
    // without an email are impossible to use. The email should be always
    // available, unless the mojo connection fails. This requires more robust
    // unit-tests.
    base::Value account_dict(base::Value::Type::DICTIONARY);
    account_dict.SetStringKey("gaiaId", account.gaia);
    account_dict.SetStringKey("email", account.email);
    account_dict.SetStringKey("name", account.full_name);
    SkBitmap account_bitmap = GetAvailableAccountBitmap(
        account.account_image, webui::GetNativeTheme(web_ui()->GetWebContents())
                                   ->ShouldUseDarkColors());
    account_dict.SetStringKey("accountImageUrl",
                              webui::GetBitmapDataUrl(account_bitmap));
    accounts_list.Append(std::move(account_dict));
  }
  FireWebUIListener("available-accounts-changed", std::move(accounts_list));
}

void ProfilePickerHandler::OnLacrosSignedInProfileCreated(
    absl::optional<SkColor> profile_color,
    Profile* profile) {
  DCHECK(lacros_sign_in_provider_);
  lacros_sign_in_provider_.reset();

  if (!profile) {
    FireWebUIListener("load-signin-finished", base::Value(/*success=*/false));
    return;
  }

  FireWebUIListener("load-signin-finished", base::Value(/*success=*/true));
  ProfilePicker::SwitchToSignedInFlow(profile_color, profile);
}

void ProfilePickerHandler::OnAccountUpserted(
    const base::FilePath& profile_path,
    const account_manager::Account& account) {
  UpdateAvailableAccounts();
}

void ProfilePickerHandler::OnAccountRemoved(
    const base::FilePath& profile_path,
    const account_manager::Account& account) {
  UpdateAvailableAccounts();
}

#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
